/*
 * ============================================================================
 *
 *       Filename:  aphash.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2014年08月20日 17时09分49秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "aphash.h"
#include "thread.h"
#include "arg.h"
#include "msg.h"
#include "process.h"

struct ap_hash_head_t *aphead = NULL;

static unsigned int 
__elfhash(unsigned char* str, unsigned int len)  
{  
	unsigned int hash = 0;  
	unsigned int x    = 0;  
	unsigned int i    = 0;  
	for(i = 0; i < len; str++, i++)  
	{  
		hash = (hash << 4) + (*str);  
		if((x = hash & 0xF0000000L) != 0)  
		{  
			hash ^= (x >> 24);  
		}  
		hash &= ~x;  
	}  
	return hash;  
}

static uint32_t 
hash_key(unsigned char *data, unsigned int len)
{
	return __elfhash(data, len) & HASH_OFFSET;
}

void hash_mackey(unsigned char *mac, uint64_t *key)
{
	*key = 0;
	memcpy(key, mac, ETH_ALEN);
}

struct ap_hash_t *hash_ap(unsigned char *mac)
{
	assert(mac != NULL);

	uint32_t key;
	uint64_t mackey;
	struct ap_hash_head_t *bucket;
	struct ap_hash_t *aphash;
	struct hlist_node *p1;

	key = hash_key(mac, ETH_ALEN);
	bucket = &aphead[key];
	hash_mackey(mac, &mackey);

	LOCK(&bucket->lock);
	/* travel bucket */
	hlist_for_each_entry(aphash, p1, &bucket->head, node) {
		if(aphash->mackey == mackey) {
			UNLOCK(&bucket->lock);
			return aphash;
		}
	}

	/* new aphash */
	aphash = safe_malloc(sizeof(struct ap_hash_t));
	aphash->mackey = mackey;
	aphash->ap.sock = -1;
	memcpy(aphash->ap.mac, mac, ETH_ALEN);
	LOCK_INIT(&aphash->msglock);
	INIT_LIST_HEAD(&aphash->msghead);
	hlist_add_head(&aphash->node, &bucket->head);

	UNLOCK(&bucket->lock);
	return aphash;
}

void aphash_init()
{
	assert(aphead == NULL);
	_Static_assert(MAX_BUCKET < (1ull << 32), "ap num too large\n");

	aphead = safe_malloc(sizeof(struct ap_hash_head_t) * MAX_BUCKET);
	printf("Max support ap: %lu\n", MAX_BUCKET);

	int i;
	for(i = 0; i < MAX_BUCKET; i++) {
		pthread_mutex_init(&aphead[i].lock, 0);
		INIT_HLIST_HEAD(&aphead[i].head);
	}
	sys_info("MODULE: aphash init success\n");
}

